/*
 * @file
 * @date 18.05.2010
 * @author Ivan Babushkin (jogrms@gmail.com)
 */

#include <X11/Xlib.h>
#include <X11/keysymdef.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <render.h>
#include <localtypes.h>
#include <default.h>
 
int Attr[] = {
	GLX_RGBA,
	GLX_RED_SIZE, 1,
	GLX_GREEN_SIZE, 1,
	GLX_BLUE_SIZE, 1,
	GLX_DEPTH_SIZE, 16,
	GLX_DOUBLEBUFFER, None
};

XVisualInfo				*xvVisualInfo;
Colormap				cmColorMap;
XSetWindowAttributes 	winAttr;
GLXContext  			glXContext;
Display 				*dpDisplay;
Window  				win;
XEvent event;
Model *model;
float posFactor;

void renderSetupModel(Model *newModel) {
	model = newModel;
}

// Main redraw function
void redraw() {
	int i;

	glClear(GL_COLOR_BUFFER_BIT);

	glColor3f(1.0f, 1.0f, 1.0f);
	glBegin(GL_LINE_STRIP);
	glVertex3f(-1.0f, -1.0f, 0.0f);
	glVertex3f(1.0f, -1.0f, 0.0f);
	glVertex3f(1.0f, 1.0f, 0.0f);
	glVertex3f(-1.0f, 1.0f, 0.0f);
	glVertex3f(-1.0f, -1.0f, 0.0f);
	glEnd();

	glColor3f(0.3f, 0.4f, 1.0f);
	glBegin(GL_POINTS);	
	for (i = 0; i < model->params.atomCount; i++) {
		glVertex3f(model->p[i].x * posFactor - 1.0f, 
				model->p[i].y * posFactor - 1.0f, 0.0f);
	}
	glEnd();

	glXSwapBuffers(dpDisplay, win); // Swap buffers, if used Double Buffer
}

// Resize function
void resize(unsigned int width, unsigned int height) {
	float projHeight;
	float projWidth;
	
	glViewport(0, 0, width, height);
	if (width > height) {
		projHeight = 1.0f;
		projWidth = (float) width / (float) height;
	}
	else {
		projWidth = 1.0f;
		projHeight = (float) height / (float) width;
	}

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-projWidth, projWidth, -projHeight, projHeight, -1.0f, 1.0f);
	glMatrixMode(GL_MODELVIEW);
}

int processEvent() {
	if (!XPending(dpDisplay)) {
		return 1;
	}

	KeySym key_symbol;
	XNextEvent(dpDisplay, &event); // Get next event in queue and copy it to XEvent structure
	switch (event.type) {
		case Expose: 
			redraw(); 
			break;

		case ConfigureNotify: 
			resize(event.xconfigure.width, event.xconfigure.height); 
			break;

		case KeyPress:
			key_symbol = XKeycodeToKeysym(dpDisplay, event.xkey.keycode, 0);
			switch (key_symbol) {
				case 'q':
					return 0;

				case 32:
					model->running = !model->running;
					break;
			}
			break;
	}
	return 1;
}

void initGLScene() {
	glPointSize(11.0f);
	glLineWidth(2.0f);
	glDisable(GL_DEPTH_TEST);
	glEnable(GL_POINT_SMOOTH);
	posFactor = 2.0f / model->params.boxSize;
}

int renderInitGLX() {

	// Connect to X Server
	dpDisplay = XOpenDisplay(NULL);
	if (dpDisplay == NULL) {
		return 1; // Could not open display
	}

	// Is GLX extension supported?
	if (!glXQueryExtension(dpDisplay, NULL, NULL)) {
		return 1; // Return, if X server haven't GLX
	}

	// Find visual
	xvVisualInfo = glXChooseVisual(dpDisplay, DefaultScreen(dpDisplay), Attr);
	if (xvVisualInfo == NULL) {
		return 1; // If Visual info can't be shoosed
	}

	// Create new colormap for our window
	cmColorMap = XCreateColormap(dpDisplay, RootWindow(dpDisplay, xvVisualInfo->screen), xvVisualInfo->visual, AllocNone);
	winAttr.colormap = cmColorMap;
	winAttr.border_pixel = 0;
	winAttr.background_pixel = 0;
	winAttr.event_mask = ExposureMask | ButtonPressMask | StructureNotifyMask | KeyPressMask;

	// Create window
	win = XCreateWindow(dpDisplay, RootWindow(dpDisplay, xvVisualInfo->screen),
			0, 0,
			640, 480, // Width and Height
			0,
			xvVisualInfo->depth, // Depth
			InputOutput,
			xvVisualInfo->visual,
			CWBorderPixel | CWColormap | CWEventMask,
			&winAttr);

	// Create OpenGL rendering context
	glXContext = glXCreateContext(dpDisplay, xvVisualInfo, None, True);
	if (glXContext == NULL) {
		return 0;
	}

	// Make it current	
	glXMakeCurrent(dpDisplay, win, glXContext);

	// Map window on the display
	XMapWindow(dpDisplay, win);
	
	initGLScene();

	return 0;
}
